package com.wodan.zhongjinjicang.common.orm.metadata;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.Map;

import com.wodan.zhongjinjicang.common.orm.RdsCommonReadDao;
import com.wodan.zhongjinjicang.common.orm.RdsCommonWriteDao;
import com.wodan.zhongjinjicang.common.orm.annotation.AutoGenernateSql;
import com.wodan.zhongjinjicang.common.orm.utils.ReflectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ObjectUtils;
import org.springframework.util.Assert;

import com.google.common.collect.Maps;

/**
 * 接口的源数据
 * 
 * @ClassName: MapperInterfaceMetaData
 * @author Administrator
 * @date 2015-11-6 下午4:18:12
 * @history
 * 
 */
public class MapperInterfaceMetaData {

	private final Class<?> mapperInterface;

	private final Map<Method, MethodMetaData> autoGenernateSqlMethodMap;

	private final Map<String, Method> superClassMethodMap;

	private final Map<String, Class<?>> genernicNameMap;

	private final Map<Method, Method> superClassMethodMapping;

	public MapperInterfaceMetaData(Class<?> clazz) {
		Assert.notNull(clazz);
		Assert.isTrue(clazz.isInterface());

		this.mapperInterface = clazz;
		this.autoGenernateSqlMethodMap = resolveAutoGenernateSqlMethods();
		this.superClassMethodMap = resolveSuperClassMethodMap();
		this.genernicNameMap = resolveGenernicNameMap();
		this.superClassMethodMapping = resolveSuperClassMethodMapping();
	}

	private Map<Method, Method> resolveSuperClassMethodMapping() {
		Map<Method, Method> resultMap = Maps.newHashMap();

		Method[] declaredMethods = mapperInterface.getDeclaredMethods();
		for (Method method : declaredMethods) {
			String methodName = method.getName();
			// 判断基类是否有相同方法
			Method superClassMethod = superClassMethodMap.get(methodName);
			if (superClassMethod == null) {
				continue;
			}

			// 判断方法参数类型是否兼容
			if (isMethodSignitureCompatible(method, superClassMethod)) {
				resultMap.put(method, superClassMethod);
			}
		}

		return resultMap;
	}

	private boolean isMethodSignitureCompatible(Method subClassMethod,
			Method superClassMethod) {
		// 判断方法名是否一致
		if (ObjectUtils.notEqual(subClassMethod.getName(),
				superClassMethod.getName())) {
			return false;
		}

		// 判断方法参数个数是否一致
		if (superClassMethod.getParameterTypes().length != subClassMethod
				.getParameterTypes().length) {
			return false;
		}

		// 判断每个方法的参数类型是否兼容
		for (int i = 0; i < subClassMethod.getParameterTypes().length; i++) {
			Class<?> parameterType = subClassMethod.getParameterTypes()[i];
			Class<?> superParameterType = superClassMethod.getParameterTypes()[i];

			if (superParameterType.equals(parameterType)) {
				continue;
			}

			Type superGenernicType = superClassMethod
					.getGenericParameterTypes()[i];
			if (superGenernicType instanceof TypeVariable) {
				String name = ((TypeVariable<?>) superGenernicType).getName();
				Class<?> actualClass = genernicNameMap.get(name);
				if (parameterType.equals(actualClass)) {
					continue;
				}
			}
		}

		return true;
	}

	private Map<String, Class<?>> resolveGenernicNameMap() {
		Class<?>[] genericInterfaces = mapperInterface.getInterfaces();
		if (ArrayUtils.isEmpty(genericInterfaces)) {
			return Collections.emptyMap();
		}

		Class<?> superInterfaces = genericInterfaces[0];

		TypeVariable<?>[] typeParameters = superInterfaces.getTypeParameters();
		if (typeParameters == null) {
			return null;
		}

		Map<String, Class<?>> map = Maps.newHashMap();
		for (int i = 0; i < typeParameters.length; i++) {
			TypeVariable<?> typeVariable = typeParameters[i];
			Class<?> interfaceGenericType = ReflectionUtils
					.getInterfaceGenericType(mapperInterface, i);
			map.put(typeVariable.getName(), interfaceGenericType);
		}

		return map;
	}

	private Map<String, Method> resolveSuperClassMethodMap() {
		Map<String, Method> map = Maps.newHashMap();
		Class<?>[] superInterfaces = mapperInterface.getInterfaces();
		if (ArrayUtils.isNotEmpty(superInterfaces)) {
			for (Class<?> superclass : superInterfaces) {
				if (RdsCommonReadDao.class.isAssignableFrom(superclass)
						|| RdsCommonWriteDao.class.isAssignableFrom(superclass)) {
					Method[] declaredMethods = superclass.getDeclaredMethods();

					for (Method method : declaredMethods) {
						String name = method.getName();
						if (map.containsKey(name)) {
							throw new IllegalStateException("同名方法已存在：["
									+ method + "]");
						} else {
							map.put(name, method);
						}
					}

				}
			}
		}
		return map;
	}

	private Map<Method, MethodMetaData> resolveAutoGenernateSqlMethods() {
		Map<Method, MethodMetaData> resultMap = Maps.newHashMap();

		Method[] allDeclaredMethods = ReflectionUtils
				.getAllDeclaredMethods(mapperInterface);
		for (Method method : allDeclaredMethods) {
			AutoGenernateSql autoGenernateSqlAnnotation = method
					.getAnnotation(AutoGenernateSql.class);
			if (autoGenernateSqlAnnotation == null) {
				continue;
			}

			MethodMetaData methodMetaData = new MethodMetaData(method);
			resultMap.put(method, methodMetaData);
		}

		return resultMap;
	}

	public boolean isAutoGenernateSql(Method method) {
		Assert.notNull(method);

		return autoGenernateSqlMethodMap.containsKey(method);
	}

	public MethodMetaData getMethodMetaData(Method method) {
		Assert.notNull(method);

		return autoGenernateSqlMethodMap.get(method);
	}

	public Class<?> getMapperInterface() {
		return mapperInterface;
	}

	public boolean isOrmFrameworkSubClass() {
		Class<?>[] superInterfaces = mapperInterface.getInterfaces();
		if (ArrayUtils.isEmpty(superInterfaces)) {
			return false;
		}
		Class<?> superclass = superInterfaces[0];
		if (RdsCommonReadDao.class.equals(superclass)) {
			return true;
		}

		if (RdsCommonWriteDao.class.equals(superclass)) {
			return true;
		}

		return false;
	}

	public boolean isOrmFrameworkMethod(Method method) {
		Assert.notNull(method);

		if (ReflectionUtils.isClassHasMethod(RdsCommonReadDao.class, method)) {
			return true;
		}

		if (ReflectionUtils.isClassHasMethod(RdsCommonWriteDao.class, method)) {
			return true;
		}

		return superClassMethodMapping.containsKey(method);
	}

	public Method getCompatibleSuperClassMethod(Method method) {
		Assert.notNull(method);

		return superClassMethodMapping.get(method);
	}
}
